// 2015年版权所有etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package raft

import (
	"errors"

	pb "go.etcd.io/etcd/raft/v3/raftpb"
	"go.etcd.io/etcd/raft/v3/tracker"
)

// 尝试单步执行本地raft消息时返回ErrStepLocalMsg 
var ErrStepLocalMsg = errors.New("raft: cannot step raft local message")

// 尝试单步执行响应消息时返回errstepperNotFound 
// 但raft中未找到对等方。该节点的prs。
var ErrStepPeerNotFound = errors.New("raft: cannot step as peer not found")

// RawNode是线程不安全的节点。
// 此结构的方法与Node的方法相对应，在这里更详细地描述了
// 方法。
type RawNode struct {
	raft       *raft
	prevSoftSt *SoftState
	prevHardSt pb.HardState
}

// NewRawNode从给定配置实例化一个RawNode。
// 
// 有关引导初始状态的信息，请参阅Bootstrap（）；这将替换此方法的前
// /'peers'参数（行为相同）。但是，建议应用程序不调用引导，而是通过设置第一个索引大于1的存储来手动引导其
// 将所需的ConfState存储为其初始状态。
// 状态，
func NewRawNode(config *Config) (*RawNode, error) {
	r := newRaft(config)	// 根据Conf工q配置信息创建raft实例
	rn := &RawNode{
		raft: r,
	}
	rn.prevSoftSt = r.softState()
	rn.prevHardSt = r.hardState()
	return rn, nil
}

// 滴答声将内部逻辑时钟提前一个滴答声。
func (rn *RawNode) Tick() {
	rn.raft.tick()
}

// TickQuiesced将内部逻辑时钟提前一个tick，而不执行任何其他状态机处理。它允许调用者避免
// 当Raft组中的所有对等方都处于相同状态时的周期性心跳和选举。预期用途是定期调用Tick 
// 或TickQuiesced，具体取决于组是“活动”还是“静止”。
// 
// 警告：使用此方法时要非常小心，因为它会破坏Raft 
// 状态机。您可能应该改为使用Tick。
func (rn *RawNode) TickQuiesced() {
	rn.raft.electionElapsed++
}

// 活动导致此RawNode转换为候选状态。
func (rn *RawNode) Campaign() error {
	return rn.raft.Step(pb.Message{
		Type: pb.MsgHup,
	})
}

// 建议将数据附加到筏形日志中。
func (rn *RawNode) Propose(data []byte) error {
	return rn.raft.Step(pb.Message{
		Type: pb.MsgProp,
		From: rn.raft.id,
		Entries: []pb.Entry{
			{Data: data},
		}})
}

// ProposeConfChange建议更改配置。请参见（节点）。
// /详细信息的变更建议。
func (rn *RawNode) ProposeConfChange(cc pb.ConfChangeI) error {
	m, err := confChangeToMsg(cc)
	if err != nil {
		return err
	}
	return rn.raft.Step(m)
}

// ApplyConfChange将配置更改应用于本地节点。应用程序在应用配置更改时必须调用
// 这一点，除非它决定拒绝
// 配置更改，在这种情况下，不得调用。
func (rn *RawNode) ApplyConfChange(cc pb.ConfChangeI) *pb.ConfState {
	cs := rn.raft.applyConfChange(cc.AsV2())
	return &cs
}

// 步骤使用给定消息推进状态机。
func (rn *RawNode) Step(m pb.Message) error {
	// 忽略通过网络接收的意外本地消息
	if IsLocalMsg(m.Type) {
		return ErrStepLocalMsg
	}
	if pr := rn.raft.prs.Progress[m.From]; pr != nil || !IsResponseMsg(m.Type) {
		return rn.raft.Step(m)
	}
	return ErrStepPeerNotFound
}

// Ready返回应用程序需要处理的未完成工作。此
// 包括附加和应用条目或快照、更新硬状态、
// 和发送消息。返回的Ready（）*必须*处理，随后
// 通过Advance（）传回。
func (rn *RawNode) Ready() Ready {
	rd := rn.readyWithoutAccept()
	rn.acceptReady(rd)
	return rd
}

// readywhithoutaccept返回一个Ready。这是一个只读操作，即
// 没有必须处理就绪的义务。
func (rn *RawNode) readyWithoutAccept() Ready {
	return newReady(rn.raft, rn.prevSoftSt, rn.prevHardSt)	// 创建Ready实例
}

// 当RawNode的使用者决定继续执行
// 并处理就绪时，调用acceptReady。
// 此调用和之前对Ready（）的调用之间的RawNode状态不得改变。
func (rn *RawNode) acceptReady(rd Ready) {
	if rd.SoftState != nil {
		rn.prevSoftSt = rd.SoftState	// prevSoftSt 记录此次返回Ready实例的SoftState状态
	}
	if len(rd.ReadStates) != 0 {
		rn.raft.readStates = nil
	}
	// 清空raft.msgs和raft.readStates
	rn.raft.msgs = nil
}

// HasReady在RawNode用户需要检查是否有任何准备就绪挂起时调用。
// 此方法中的检查逻辑应与Ready一致。containsUpdates（）。
func (rn *RawNode) HasReady() bool {
	r := rn.raft
	if !r.softState().equal(rn.prevSoftSt) {
		return true
	}
	if hardSt := r.hardState(); !IsEmptyHardState(hardSt) && !isHardStateEqual(hardSt, rn.prevHardSt) {
		return true
	}
	if r.raftLog.hasPendingSnapshot() {
		return true
	}
	if len(r.msgs) > 0 || len(r.raftLog.unstableEntries()) > 0 || r.raftLog.hasNextEnts() {
		return true
	}
	if len(r.readStates) != 0 {
		return true
	}
	return false
}

// Advance通知RawNode应用程序已在
// last Ready results中应用并保存进度。
func (rn *RawNode) Advance(rd Ready) {
	if !IsEmptyHardState(rd.HardState) {
		rn.prevHardSt = rd.HardState
	}
	rn.raft.advance(rd)
}

// Status返回给定组的当前状态。此分配，请参见
// BasicStatus和WithProgress以了解更友好的分配选择。
func (rn *RawNode) Status() Status {
	status := getStatus(rn.raft)
	return status
}

// BasicStatus返回一个BasicStatus。值得注意的是，这不包含
// 进度图；请参阅WithProgress，了解无需分配的检查方法。
func (rn *RawNode) BasicStatus() BasicStatus {
	return getBasicStatus(rn.raft)
}

// ProgressType表示进度对应的副本类型。
type ProgressType byte

const (
	// ProgressTypePeer伴随常规对等副本的进度。
	ProgressTypePeer ProgressType = iota
	// ProgressTypeLearner为学习者副本伴随进度。
	ProgressTypeLearner
)

// WithProgress是一个帮助程序，用于检查此节点及其
// /同龄人。
func (rn *RawNode) WithProgress(visitor func(id uint64, typ ProgressType, pr tracker.Progress)) {
	rn.raft.prs.Visit(func(id uint64, pr *tracker.Progress) {
		typ := ProgressTypePeer
		if pr.IsLearner {
			typ = ProgressTypeLearner
		}
		p := *pr
		p.Inflights = nil
		visitor(id, typ, p)
	})
}

// 报告无法访问报告上次发送时无法访问给定节点。
func (rn *RawNode) ReportUnreachable(id uint64) {
	_ = rn.raft.Step(pb.Message{Type: pb.MsgUnreachable, From: id})
}

// ReportSnapshot报告已发送快照的状态。
func (rn *RawNode) ReportSnapshot(id uint64, status SnapshotStatus) {
	rej := status == SnapshotFailure

	_ = rn.raft.Step(pb.Message{Type: pb.MsgSnapStatus, From: id, Reject: rej})
}

// TransferLeader试图将领导权转移给给定的受让人。
func (rn *RawNode) TransferLeader(transferee uint64) {
	_ = rn.raft.Step(pb.Message{Type: pb.MsgTransferLeader, From: transferee})
}

// ReadIndex请求读取状态。读取状态将设置为就绪。
// 读取状态具有读取索引。一旦应用程序的进程超过读取
// 索引，在读取请求之前发出的任何可线性化的读取请求都可以安全地处理。读取状态将连接相同的rctx。
func (rn *RawNode) ReadIndex(rctx []byte) {
	_ = rn.raft.Step(pb.Message{Type: pb.MsgReadIndex, Entries: []pb.Entry{{Data: rctx}}})
}
